﻿// Global facade object to provide common functionality
define([
    'Wjs/config',
    'Wjs/core/array',
    'Wjs/core/types',
    'Wjs/core/typeBuilder',
    'Wjs/core/typeManager',
    'Wjs/core/instanceManager',
    'Wjs/util/Template'
],
function (conf, array, types, typeBuilder, typeManager, instanceManager, Template) {
    'use strict';

    var host = window;

    if (host.Wjs) {

        console.log('Wjs module has been already loaded');
        return; // It has been already loaded
    }

    console.log('Entering Wjs module');

    host.Wjs = {

        array: array,

        // Facade functions using the core modules

        // Defines a class
        define: function (conf) {

            return typeBuilder.define(conf);
        },

        // If the type (function construct) is already defined return it from the cache, otherwise add it to the cache and return it
        defineOnce: function (conf) {

            return typeBuilder.defineOnce(conf);
        },

        // Chains an existing function adding the functionality after calling the original one
        chain: function (first, second, scope) {

            return typeBuilder.chain(first, second, scope);
        },

        // Chains an existing function adding the functionality after calling the original one
        chainQuitOnFalse: function (first, second, scope) {

            return typeBuilder.chainQuitOnFalse(first, second, scope);
        },

        copyMembers: function (from, to, options) {

            typeBuilder.copyMembers(from, to, options);
        },

        //clone: function(o) {

        //    var r = {};

        //    for (var name in o) { // Copy absolutely everything

        //        //if (types.isObject(o[name]) &&
        //        //    !types.isString(o[name]) &&
        //        //    !types.isFunction(o[name])) { // Clone the member as well

        //        //    r[name] = this.clone(o[name]);
        //        //}
        //        //TODO: Handle arrays
        //        //else {

        //            r[name] = o[name];
        //        //}

        //    }

        //    return r;
        //},

        createEnumeration: function (cfg) {

            return typeBuilder.createEnumeration(cfg);
        },

        dataFromEnumeration: function (enumeration, textField, valueField) {
            var output = [],
                data;

            for (var name in enumeration) {

                data = {};

                data[textField] = name;
                data[valueField] = enumeration[name];

                output.push(data);
            }

            return output;
        },

        // Returns all the mixins attached to this type and the base ones
        getMixins: function (o) {

            return typeBuilder.getMixins(o);
        },

        // Registers a type
        registerType: function (type) {

            typeManager.registerType(type);
        },

        // Retrieves the type 
        getType: function (type, optional) {

            return typeManager.getType(type, optional);
        },

        // Creates an instance of an object. If the instance has an id, it will be registered by the instance manager
        create: function (conf, type, parent) {
            var instance = typeManager.create(conf, type, parent);

            if (instance.id) { // If the instance has an id, then register it with the instance manager
                instanceManager.register(instance);
            }

            return instance;
        },

        'new': function (type) {

            return this.create({}, type);
        },

        // Registers an instance by its id
        register: function (instance, id) {

            return instanceManager.register(instance, id);
        },

        // Retrieves a cached instance by its id
        get: function (id, conf) {

            return instanceManager.get(id, conf);
        },

        // Removes a cached instance by its id
        remove: function (id) {

            return instanceManager.remove(id);
        },

        compile: function (source) { // Attach a function to compile the default template to the global object

            if (!this.template) {
                this.template = new Template();
            }

            return this.template.compile(source);
        },

        // Delays the execution of code for a given time in milliseconds
        delay: function (fn, time, scope) {
            var timeout,
                f;

            time = time || 0;
            scope = scope || this;

            f = function () {

                fn.call(scope);

                clearTimeout(timeout);
            };

            timeout = setTimeout(f, time);

            return {
                cancel: function () {

                    clearTimeout(timeout);

                    timeout = null;
                }
            }
        },

        wait: function (test, callback, time, scope) {

            var interval,
                f;

            time = time || 0;
            scope = scope || this;

            f = function () {

                if (test.call(scope) === true) { // Condition satisfied

                    clearInterval(interval); // Done, cleanup
                    callback.call(scope);
                }

            };

            interval = setInterval(f, time);
        },

        //// Wrapper to use a message box as an alert 
        //alert: function (conf) {

        //    if (Wjs.messageBox) {

        //        Wjs.messageBox.show(conf);
        //    }
        //    else {

        //        console.log('To enable message box functionality, include messageBox module in your main script');

        //        alert(conf.text);
        //    }
        //},

        log: function (flag, text) {

            if (this[flag]) {

                console.log(this[flag] + ': ' + text);
            }
        },

        loadJavascript: function (src, callback) {
            var firstScript = window.document.getElementsByTagName("script")[0],
                script = window.document.createElement("script");

            script.src = src;
            script.async = true;

            firstScript.parentNode.insertBefore(script, firstScript);

            if (callback) {

                script.onload = callback;
            }
        },

        bind: function (fcn, scope) {

            return fcn.bind(scope);
        }
    };

    typeBuilder.copyMembers(types, Wjs); // Copy all the functions from type to the global object

    if (conf && conf.apply) {

        conf.apply.call(Wjs, conf);
    }
});